using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;

namespace AdvancedInspector
{
	[AdvancedInspector]
	public abstract class ComponentMonoBehaviour : MonoBehaviour
	{
		[SerializeField]
		private MonoBehaviour owner;

		public MonoBehaviour Owner
		{
			get
			{
				return owner;
			}
			set
			{
				if (value != null)
				{
					owner = value;
				}
			}
		}

		protected virtual void Reset()
		{
			base.hideFlags = HideFlags.HideInInspector;
		}

		public void Erase()
		{
			foreach (FieldInfo field in GetFields(GetType(), false))
			{
				object value = field.GetValue(this);
				if (value is ComponentMonoBehaviour)
				{
					ComponentMonoBehaviour componentMonoBehaviour = value as ComponentMonoBehaviour;
					if (componentMonoBehaviour.Owner == Owner)
					{
						componentMonoBehaviour.Erase();
					}
				}
			}
			UnityEngine.Object.DestroyImmediate(this, true);
		}

		public ComponentMonoBehaviour Instantiate()
		{
			return Instantiate(base.gameObject, Owner);
		}

		public ComponentMonoBehaviour Instantiate(MonoBehaviour owner)
		{
			return Instantiate(base.gameObject, owner);
		}

		public ComponentMonoBehaviour Instantiate(GameObject go, MonoBehaviour owner)
		{
			return CopyObject(go, owner, this) as ComponentMonoBehaviour;
		}

		private static object CopyObject(GameObject go, MonoBehaviour owner, object original)
		{
			if (original == null)
			{
				return null;
			}
			Type type = original.GetType();
			if (type == typeof(string))
			{
				return ((string)original).Clone();
			}
			if (type.Namespace == "System")
			{
				return original;
			}
			if (typeof(IList).IsAssignableFrom(type))
			{
				return CopyList(go, owner, (IList)original);
			}
			if (typeof(ComponentMonoBehaviour).IsAssignableFrom(type))
			{
				return CopyComponent(go, owner, (ComponentMonoBehaviour)original);
			}
			if (typeof(Component).IsAssignableFrom(type))
			{
				return null;
			}
			if (typeof(ScriptableObject).IsAssignableFrom(type))
			{
				return UnityEngine.Object.Instantiate((ScriptableObject)original);
			}
			if (typeof(UnityEngine.Object).IsAssignableFrom(type))
			{
				return original;
			}
			if (type.IsClass)
			{
				return CopyClass(go, owner, original);
			}
			return original;
		}

		private static IList CopyList(GameObject go, MonoBehaviour owner, IList original)
		{
			Type type = original.GetType();
			IList list = ((!type.IsArray) ? (Activator.CreateInstance(type) as IList) : Array.CreateInstance(type.GetElementType(), original.Count));
			for (int i = 0; i < original.Count; i++)
			{
				list[i] = CopyObject(go, owner, original[i]);
			}
			return list;
		}

		private static ComponentMonoBehaviour CopyComponent(GameObject go, MonoBehaviour owner, ComponentMonoBehaviour original)
		{
			Type type = original.GetType();
			ComponentMonoBehaviour componentMonoBehaviour = go.AddComponent(original.GetType()) as ComponentMonoBehaviour;
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			foreach (FieldInfo fieldInfo in fields)
			{
				fieldInfo.SetValue(componentMonoBehaviour, CopyObject(go, componentMonoBehaviour, fieldInfo.GetValue(original)));
			}
			componentMonoBehaviour.Owner = owner;
			return componentMonoBehaviour;
		}

		private static object CopyClass(GameObject go, MonoBehaviour owner, object original)
		{
			Type type = original.GetType();
			object obj = Activator.CreateInstance(type);
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			foreach (FieldInfo fieldInfo in fields)
			{
				fieldInfo.SetValue(obj, CopyObject(go, owner, fieldInfo.GetValue(original)));
			}
			return obj;
		}

		private static List<FieldInfo> GetFields(Type type, bool recursive)
		{
			List<FieldInfo> list = ((!recursive) ? type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).ToList() : type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic).ToList());
			if (type.BaseType != null && type.BaseType != typeof(object))
			{
				list.AddRange(GetFields(type.BaseType, true));
			}
			return list;
		}
	}
}
